package cn.flython.yaml;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;

public class YamlApplication {

    private static final String EOF_KEY = "$EOF";

    public static void main(String[] args) throws IOException {
        assert args.length == 2;


        var orginName = args[0];
        var updateName = args[1];
        assert orginName != null;
        assert updateName != null;

        int shellRes = updateYamlFile(orginName, updateName);
        System.exit(shellRes);
    }


    private static int updateYamlFile(String orginName, String updateName) throws IOException {

        var orginFile = new File(orginName);
        var updateFile = new File(updateName);


        DumperOptions dumperOptions = new DumperOptions();
        //设置yaml格式 我们常用的那种 DumperOptions.FlowStyle.BLOCK
        dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        //设置文本量的输出格式 建议使用 DumperOptions.ScalarStyle.PLAIN（啥都不带,默认值）
        dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
        Yaml yaml = new Yaml(dumperOptions);

        Map<String, Object> orgin = yaml.load(new FileInputStream(orginFile));
        Map<String, Object> newYaml = yaml.load(new FileInputStream(updateFile));

        var toDump = mergeYamlMap(orgin, newYaml);


        File backupFile = getBackupFile(orginFile);
        if (backupFile.exists()) {
            backupFile.delete();
        }
        Files.copy(orginFile.toPath(), backupFile.toPath());

        var newFile = orginFile;
        try (var fw = new FileWriter(newFile)) {
            yaml.dump(toDump, fw);
        }
        supplyComment(newFile);
        return 0;
    }

    private static File getBackupFile(File orginFile) {
        var orginFileName = orginFile.getName();
        int lastIndexOfDot = orginFileName.lastIndexOf(".");
        var fileNameNoExtension = orginFileName.substring(0, lastIndexOfDot);
        var extension = orginFileName.substring(lastIndexOfDot);
        var newName = fileNameNoExtension + "-old" + extension;
        var newFile = new File((orginFile.getParent() == null ? "." : orginFile.getParent()) + File.separator + newName);
        return newFile;
    }

    private static Map<String, Object> mergeYamlMap(Map<String, Object> orginYaml, Map<String, Object> newYaml) {
        if (orginYaml == null) {
            return newYaml;
        }

        newYaml.forEach((k, v) -> {
            var oldVal = orginYaml.get(k);
            if (oldVal == null) {
                orginYaml.put(k, v);
            } else if (oldVal instanceof Map) {
                mergeYamlMap((Map<String, Object>) oldVal, (Map<String, Object>) v);
            }
        });

        return orginYaml;
    }

    private static void supplyComment(File meargedFile) throws IOException {
        HashMap<String, Stack<String>> commentMap = readComment(getBackupFile(meargedFile));
        writeComment(meargedFile, commentMap);
    }

    private static HashMap<String, Stack<String>> readComment(File file) throws IOException {

        String[] lines = readFilesAsLines(file);

        HashMap<String, Stack<String>> commentMap = new HashMap<>();

        for (int i = 0, linesLength = lines.length; i <= linesLength; i++) {
            String line = lines[i];

            if (line.trim().startsWith("#")) {
                Stack<String> stack = commentMap.compute((i == linesLength) ? EOF_KEY : lines[i + 1], (k, v) -> {
                    if (v == null) {
                        var ns = new Stack<String>();
                        return ns;
                    } else {
                        return v;
                    }
                });
                stack.push(line);
            }

        }

        return commentMap;
    }

    private static void writeComment(File file, HashMap<String, Stack<String>> commentMap) throws IOException {
        String[] lines = readFilesAsLines(file);
        var outputList = new LinkedList<String>();
        var linesLength = lines.length;
        Stack<String> eofComment = commentMap.remove(EOF_KEY);

        if (eofComment != null) {
            eofComment.forEach(outputList::addFirst);
        }

        for (int i = linesLength - 1; i >= 0; i--) {
            String line = lines[i];
            while (line != null) {
                outputList.addFirst(line);
                Stack<String> stack = commentMap.get(line);
                if (stack == null) {
                    break;
                }
                if (stack.size() == 1) {
                    commentMap.remove(line);
                }
                line = stack.pop();

            }
        }

        try (var bw = new BufferedWriter(new FileWriter(file))) {
            outputList.forEach(line -> {
                try {
                    bw.append(line).append('\n');
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            bw.flush();
        }


    }

    private static String[] readFilesAsLines(File file) throws IOException {
        FileReader fileReader = new FileReader(file);
        //注意！！流只能用一次，第二次是个空的流了！！
        try (var br = new BufferedReader(fileReader)) {
            String[] lines = br.lines().toArray(String[]::new);
            return lines;
        }
    }

}
